package com.zhoug.player3.exo;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.view.TextureView;


import com.zhoug.common3.Logger;
import com.zhoug.player3.core.BasePlayer;
import com.zhoug.player3.core.PlayError;
import com.zhoug.player3.core.ui.VideoRatioTextureView;
import com.zhoug.player3.core.ZPlayer;
import com.zhoug.player3.core.utils.PlayUrlUtils;

import java.util.Locale;

import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.MediaItem;
import androidx.media3.common.MimeTypes;
import androidx.media3.common.PlaybackException;
import androidx.media3.common.Player;
import androidx.media3.common.VideoSize;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.datasource.DataSource;
import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.exoplayer.hls.DefaultHlsDataSourceFactory;
import androidx.media3.exoplayer.hls.DefaultHlsExtractorFactory;
import androidx.media3.exoplayer.hls.HlsMediaSource;
import androidx.media3.exoplayer.source.MediaSource;
import androidx.media3.extractor.ts.DefaultTsPayloadReaderFactory;

import static androidx.media3.common.Player.EVENT_IS_PLAYING_CHANGED;
import static androidx.media3.common.Player.EVENT_PLAYBACK_STATE_CHANGED;
import static androidx.media3.common.Player.EVENT_PLAY_WHEN_READY_CHANGED;
import static androidx.media3.common.Player.EVENT_POSITION_DISCONTINUITY;
import static androidx.media3.common.Player.EVENT_TIMELINE_CHANGED;


/**
 * exoplayer 播放器封装
 *
 * @Author: zhoug
 * @Date: 2022/12/16
 * @Description:
 */

@UnstableApi
public class ExoPlayerHelper extends BasePlayer {
    private static final String TAG = ">>>ExoPlayerHelper";
    private ExoPlayer mPlayer;
    private Handler mMainHandler = new Handler(Looper.getMainLooper());
    private boolean detectAccessUnits;

    public ExoPlayerHelper() {
    }

    public ExoPlayerHelper(Context context) {
        mContext = context;
    }

    /**
     * 某些MPEG-TS文件不包含访问单元分隔符（AUD）。默认情况下，ExoPlayer依靠AUD来便宜地检测帧边界。
     * 同样，某些MPEG-TS文件不包含IDR关键帧。默认情况下，这些是ExoPlayer考虑的唯一关键帧类型。
     * 当被要求播放缺少AUD或IDR关键帧的MPEG-TS文件时，ExoPlayer缓冲会报错
     * 如果需要播放此类文件 设置为true
     *FLAG_ALLOW_NON_IDR_KEYFRAMES
     * FLAG_DETECT_ACCESS_UNITS
     * @param detectAccessUnits
     */
    public void setDetectAccessUnits(boolean detectAccessUnits) {
        this.detectAccessUnits = detectAccessUnits;
    }


    @Override
    public void setMute(boolean mute) {
        super.setMute(mute);
        _setMute();
    }


    @Override
    public void setVideoTextureView(@Nullable TextureView textureView) {
        super.setVideoTextureView(textureView);
        _setTextureView();
    }

    @Override
    public void startPlay() {
        //flv rtmp 未支持
        if (TextUtils.isEmpty(mPlayUrl)) {
            callError(PlayError.URL_IS_EMPTY, "playUrl为空");
            return;
        }
        if (mPlayer == null) {
            initPlayer();
            if (mPlayer == null) {
                callError(PlayError.PLAYER_INIT_FAILURE, "初始化播放器失败");
                return;
            }
        }
        MediaItem.Builder mediaItemBuilder = new MediaItem.Builder().setUri(mPlayUrl);
        MediaSource mediaSource = null;
        int type = PlayUrlUtils.getUrlType(mPlayUrl);
        if (type == PlayUrlUtils.TYPE_HLS) {
            Logger.d(TAG, "startPlay:HLS");
            mediaItemBuilder.setMimeType(MimeTypes.APPLICATION_M3U8);
            DataSource.Factory httpDataSourceFactory = ExoPlayerFactory.getDefaultHttpDataSourceFactory();
            DefaultHlsDataSourceFactory hlsDataSourceFactory = new DefaultHlsDataSourceFactory(httpDataSourceFactory);
            HlsMediaSource.Factory hlsfactory = new HlsMediaSource.Factory(hlsDataSourceFactory);
            hlsfactory.setAllowChunklessPreparation(true);
            DefaultHlsExtractorFactory defaultHlsExtractorFactory = null;
            if (detectAccessUnits) {
                defaultHlsExtractorFactory = new DefaultHlsExtractorFactory(DefaultTsPayloadReaderFactory.FLAG_DETECT_ACCESS_UNITS, true);
            } else {
                defaultHlsExtractorFactory = new DefaultHlsExtractorFactory();
            }
            hlsfactory.setExtractorFactory(defaultHlsExtractorFactory);
            mediaSource = hlsfactory.createMediaSource(mediaItemBuilder.build());


        } else if (type == PlayUrlUtils.TYPE_RTSP || type == PlayUrlUtils.TYPE_RTMP) {
            Logger.d(TAG, "startPlay:RTSP");
            mediaItemBuilder.setMimeType(MimeTypes.APPLICATION_RTSP);
//            mediaSource = MediaSourceFactory.createRtspMediaSource(mediaItemBuilder.build());
        } else if (type == PlayUrlUtils.TYPE_FLV) {
            Logger.d(TAG, "startPlay:FLV");
        } else if (type == PlayUrlUtils.TYPE_LOCAL) {
            Logger.d(TAG, "startPlay:LOCAL");
//            mediaSource = new ProgressiveMediaSource.Factory(new FileDataSource.Factory())
//                    .createMediaSource(mediaItemBuilder.build());

        }


        if (mediaSource != null) {
            mPlayer.setMediaSource(mediaSource);
        } else {
            mPlayer.setMediaItem(mediaItemBuilder.build());
        }
//        mExoPlayer.seekTo(5000);

        mPlayer.prepare();
        mPlayer.play();

        Logger.d(TAG, "startPlay:开始播放:" + mPlayUrl);
        callStatusChange(ZPlayer.STATE_START);
    }

    @Override
    public void stopPlay() {
        if (mPlayer != null) {
            Logger.d(TAG, "stopPlay:停止播放");
            mPlayer.stop();
        }
        stopBufferLoading();
        cancelProgressUpdate();
    }

    @Override
    public void pausePlay() {
        if (mPlayer != null && mPlayer.isPlaying()) {
            Logger.d(TAG, "pausePlay:暂停播放");
            mPlayer.pause();
            callStatusChange(ZPlayer.STATE_PAUSE);
        } else {
            stopPlay();
        }
        stopBufferLoading();
        cancelProgressUpdate();
    }

    @Override
    public void resumePlay() {
        if (mPlayer != null) {
            if (mPlayer.isPlaying()) {
                Logger.d(TAG, "resumePlay:正在播放,无需start");
//                invokeStatusChange(ZPlayer.STATE_PLAYING);
                return;
            }
            int playbackState = mPlayer.getPlaybackState();
            if (playbackState == Player.STATE_BUFFERING) {
                Logger.d(TAG, "resumePlay:恢复播放");
                mPlayer.play();
                return;
            } else if (playbackState == Player.STATE_READY) {
                Logger.d(TAG, "resumePlay:恢复播放");
                mPlayer.play();
                callStatusChange(ZPlayer.STATE_PLAYING);
                return;
            }
        }
        startPlay();
    }

    /**
     * 设置指定位置播放
     *
     * @param positionMs
     */
    @Override
    public void seekTo(long positionMs) {
        if (mPlayer != null) {
            mPlayer.seekTo(positionMs);
        }
    }

    /**
     * 切换为直播
     */
    @Override
    public void switchToLive() {

    }

    private void _setMute() {
        if (mPlayer != null) {
            mPlayer.setVolume(mMute ? 0 : 1);
        }
    }

    private void _setTextureView() {
        if (mPlayer != null) {
            if (mTextureView != null) {
                mTextureView.setKeepScreenOn(true);
            }
            mPlayer.setVideoTextureView(mTextureView);
        }

    }

    private void initPlayer() {
        if (mPlayer == null) {
            synchronized (this) {
                if (mPlayer == null) {
                    Logger.d(TAG, "initPlayer:初始化播放器");
                    try {
                        mPlayer = ExoPlayerFactory.createExoPlayer(mContext);
                        ExoPlayerInit.initDebug(mPlayer);
                        _setTextureView();
//                        StyledPlayerView styledPlayerView;
//                        styledPlayerView.setResizeMode();
                        mPlayer.addListener(mPlayerListener);
//                        mExoPlayer.addAnalyticsListener(new ExoPlayerAnalyticsListenerWrapper());
                        _setMute();
                    } catch (Exception e) {
                        e.printStackTrace();
                        callError(PlayError.PLAYER_INIT_FAILURE, "初始化播放器失败:" + e.getMessage());
                    }
                }
            }
        }
    }


    /**
     * 播放监听
     */
    private Player.Listener mPlayerListener = new ExoPlayerListenerWrapper() {

        /**
         * 当一个或多个Player状态更改时调用
         *
         * @param player
         * @param events
         */
        @Override
        public void onEvents(Player player, Player.Events events) {
            super.onEvents(player, events);
            if (events.containsAny(
                    EVENT_PLAYBACK_STATE_CHANGED, EVENT_PLAY_WHEN_READY_CHANGED, EVENT_IS_PLAYING_CHANGED)) {
                startProgressUpdate();
            }
            if (events.containsAny(EVENT_POSITION_DISCONTINUITY, EVENT_TIMELINE_CHANGED)) {
                if (mOnPlayProgressListener != null) {
                    long duration = mPlayer.getDuration();
                    if (duration != C.TIME_UNSET) {
                        mOnPlayProgressListener.onDurationChange(duration);
                    }
                }
            }


        }

        /**
         * getPlaybackState()播放状态改变时调用
         * @param playbackState
         */
        @Override
        public void onPlaybackStateChanged(int playbackState) {
            super.onPlaybackStateChanged(playbackState);
            switch (playbackState) {
                case Player.STATE_IDLE://空闲状态,
                    Logger.d(TAG, "空闲状态");
                    stopBufferLoading();
                    callStatusChange(ZPlayer.STATE_IDLE);
                    break;
                case Player.STATE_BUFFERING://还没有播放,正在缓冲数据
                    Logger.d(TAG, "正在缓冲数据");
                    startBufferLoading();
                    break;
                case Player.STATE_READY://可以播放了,如果getPlayWhenReady()为true则会播放否则暂停
                    Logger.d(TAG, "可以播放了");
                    if (mPlayer.isPlaying()) {
                        callStatusChange(ZPlayer.STATE_PLAYING);
                    } else {
                        callStatusChange(ZPlayer.STATE_PREPARED);
                    }
                    startProgressUpdate();
                    break;
                case Player.STATE_ENDED:
                    Logger.d(TAG, "播放完成");
                    stopBufferLoading();
                    callStatusChange(ZPlayer.STATE_ENDED);
                    break;
            }


        }

        /**
         * 播放错误时调用,状态置为:STATE_IDLE
         * @param error
         */
        @Override
        public void onPlayerError(PlaybackException error) {
            super.onPlayerError(error);
            stopBufferLoading();
            if (error != null) {
                String errorMsg = error.getErrorCodeName();
                switch (error.errorCode) {
                    case PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED:
                        errorMsg = "网络连接失败";
                        callError(PlayError.NETWORK_CONNECTION_FAILED, "网络连接失败");
                        break;
                    case PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW:
                        //如果播放器暂停或缓冲足够长的时间，则播放位置可能会落在实时窗口后面。如果发生这种情况，则播放将失败
                        //尝试重新播放
                        startPlay();
                        break;
                    case PlaybackException.ERROR_CODE_IO_BAD_HTTP_STATUS:
                        callError(PlayError.BAD_HTTP_STATUS, "错误的HTTP状态");
                        break;
                    case PlaybackException.ERROR_CODE_IO_UNSPECIFIED:
                        //由无法识别的输入/输出错误引起
                        callError(PlayError.BAD_HTTP_STATUS, "读取媒体数据时输入输出错误");
                        break;
                    default:
                        callError(PlayError.UNKNOWN_ERROR, "errorCode=" + error.errorCode + ",errorMsg=" + errorMsg);
                        break;
                }
            } else {
                callError(PlayError.UNKNOWN_ERROR, "未知错误");
            }
        }

        /**
         * 每次渲染的视频大小发生变化时调用
         *
         * @param videoSize
         */
        @Override
        public void onVideoSizeChanged(VideoSize videoSize) {
            super.onVideoSizeChanged(videoSize);
            if (videoSize == null) return;
            if (mOnPlayListener != null) {
                mOnPlayListener.onVideoSizeChanged(videoSize.width, videoSize.height);
            }
            if (mTextureView instanceof VideoRatioTextureView) {
                float videoRatio = videoSize.width * 1.0f / videoSize.height;
                ((VideoRatioTextureView) mTextureView).setRatio(videoRatio);
            }
        }


    };


    /**
     * 开始录制视频
     *
     * @param path 保存的视频地址
     */
    @Override
    public void startRecordVideo(String path) {
        Logger.e(TAG, "startRecordVideo:未实现视频录制");
    }

    /**
     * 停止录制视频
     */
    @Override
    public void stopRecordVideo() {
        Logger.e(TAG, "startRecordVideo:未实现视频录制");
    }


    /**
     * 开启对讲
     */
    @Override
    public void startVoiceTalk() {

    }

    /**
     * 停止对讲
     */
    @Override
    public void stopVoiceTalk() {

    }

    /**
     * 是否正在对讲
     *
     * @return
     */
    @Override
    public boolean isTalking() {
        return false;
    }

    private static final int PROGRESS_UPDATE_INTERVAL = 500;

    //更新进度
    private Handler mProgressHandler = new Handler(Looper.getMainLooper());

    protected void startProgressUpdate() {
        mProgressHandler.removeCallbacks(progressRunnable);
        mProgressHandler.postDelayed(progressRunnable, PROGRESS_UPDATE_INTERVAL);
    }

    private void cancelProgressUpdate() {
        mProgressHandler.removeCallbacks(progressRunnable);
    }

    private Runnable progressRunnable = new Runnable() {
        @Override
        public void run() {
            if (mPlayer == null) return;
            if (mOnPlayProgressListener != null) {
                long position = mPlayer.getCurrentPosition();
                if (position != C.TIME_UNSET) {
                    mOnPlayProgressListener.onPositionChange(position);
                }
            }
            int playbackState = mPlayer.getPlaybackState();
            if (playbackState != Player.STATE_ENDED && playbackState != Player.STATE_IDLE) {
                mProgressHandler.postDelayed(progressRunnable, PROGRESS_UPDATE_INTERVAL);
            }
        }
    };


    private void test() {


    }

    @Override
    public void release() {
        super.release();
        if (mPlayer != null) {
            Logger.d(TAG, "release:释放资源:" + mPlayer);
            cancelProgressUpdate();
            stopBufferLoading();
            mPlayer.removeListener(mPlayerListener);
            mPlayer.release();
            mPlayer = null;
        }
    }


    private Runnable bufferTask = new Runnable() {
        private int lastBufferedPercentage = 0;

        @Override
        public void run() {
            //返回当前内容或广告中数据缓冲的位置估计值，单位为毫秒
//            long bufferedPosition = mPlayer.getBufferedPosition();
            //百分比
            int bufferedPercentage = mPlayer.getBufferedPercentage();
            //如果是广告则是广告的数据缓冲位置否则和getBufferedPosition一样
//            long contentBufferedPosition = mPlayer.getContentBufferedPosition();
            //返回当前位置的总缓冲持续时间的估计值，单位为毫秒。这包括用于后续广告和媒体项目的预缓冲数据
//            long totalBufferedDuration = mPlayer.getTotalBufferedDuration();
            if (lastBufferedPercentage != bufferedPercentage) {
                lastBufferedPercentage = bufferedPercentage;
                Logger.d(TAG, String.format(Locale.CHINA, "缓冲:%d%%", lastBufferedPercentage));
            }
            if (lastBufferedPercentage < 100) {
                mMainHandler.postDelayed(bufferTask, 200);
            }
        }
    };

    private void startBufferLoading() {
        mMainHandler.removeCallbacks(bufferTask);
        mMainHandler.post(bufferTask);
    }

    private void stopBufferLoading() {
        mMainHandler.removeCallbacks(bufferTask);
    }
}
